package com.github.livebase.util;

import com.github.livebase.util.base.ByteArray;
import com.github.livebase.util.base.IOUtil;
import com.github.livebase.util.http.ClientUtil;
import com.github.livebase.util.http.CloseableResponse;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @ClassName LFile
 * @description:
 * @author: biao532
 * @create: 2024-06-13 11:25
 * @Version 1.0
 **/
public class FileUtil {

    public static File link(File file, LiveContext context) throws IOException {
        if ( !file.exists() || !file.isFile() || !file.getName().endsWith(".link") ) {
            return file;
        }

        try(FileInputStream inputStream = new FileInputStream(file)) {
            String url = IOUtil.toString(inputStream);
            StringKeyValuePairs kvp = StringKeyValuePairs.createKeyValuePairs(url, "#");
            url = kvp.getKey();

            String md5 = EncryptUtil.md5Hex(url);

            File cacheFile = new File(context.getCacheDir(), md5);
            if (cacheFile.exists()) {
                cacheFile.delete();
            }
            URL u = new URL(url);
            try (InputStream s = openStream(u)) {
                if (s == null) {
                    throw new IOException("can't find the file that url is " + u);
                }
                try (OutputStream outputStream = new FileOutputStream(cacheFile)){
                    cacheFile.createNewFile();
                    IOUtil.transferTo(s, outputStream);
                }

                if (!kvp.getValue().isEmpty()) {
                    byte[] chk = md5(cacheFile);
                    if (!IOUtil.toHex(chk).equalsIgnoreCase(kvp.getValue())) {
                        throw new IOException("the file md5 is not match");
                    }
                }
                return cacheFile;
            }

        } catch (IOException | ExecutionException | InterruptedException | NoSuchAlgorithmException e) {
            throw new IOException("an error occurred when get real link file " + file);
        }
    }

    private static InputStream openStream(URL url) throws ExecutionException, InterruptedException, FileNotFoundException {
        if ("http".equalsIgnoreCase(url.getProtocol()) || "https".equalsIgnoreCase(url.getProtocol())) {
            FutureTask<InputStream> f = new FutureTask<>(()-> {
                CloseableResponse response = ClientUtil.sendHttpRequest("GET", url, Collections.emptyList(), null);
                if (response != null && response.getCode() == 200) {
                    return response.getStream();
                }
                return null;
            });
            new Thread(f).start();
            return f.get();
        }
        if ("file".equalsIgnoreCase(url.getProtocol())) {
            return new FileInputStream(url.getPath());
        }
        return null;
    }

    public static byte[] md5(File file) throws IOException, NoSuchAlgorithmException {
        try (FileInputStream input = new FileInputStream(file)){
            ByteArray array = IOUtil.transferToByteArray(input, -1);
            return EncryptUtil.md5Hex(array.toArray());
        }
    }
}
